/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.editor;
import java.util.Map;
import java.util.List;
import java.util.Iterator;
import java.util.HashMap;
import java.util.ArrayList;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import javax.swing.text.JTextComponent;
/**
* Configurable settings that editor uses. All the methods are static
* The editor is configurable mainly by using the following static
* method in Settings class:
*
* org.netbeans.editor.Settings.setValue(Class kitClass, String settingName, Object newValue);
*
* kitClass - this is the class of the editor kit for which the setting is changed.
* The current hierarchy of editor kits is as follows:
* <tt>org.netbeans.editor.BaseKit</tt> - this is the base kit, the begining of the whole
* kit hierarchy.
* <tt>org.netbeans.editor.ext.PlainKit</tt> - this is the kit that extends the BaseKit
* and it's used for plain text editing
* <tt>org.netbeans.editor.ext.JavaKit</tt> - this kit also extends the BaseKit
* and it's used for java files editing
*
* When the particular setting is not set for a given kit, then the superclass of
* the given kit class is retrieved and the search for the setting value is performed.
* Example: If the java document calls Settings.getValue() to retrieve the value
* for TAB_SIZE setting and it passes JavaKit.class as the kitClass
* parameter and the setting has no value on this level, then the super class
* of the JavaKit is retrieved (by using Class.getSuperclass() call) which is BaseKit
* in this case and the search for the value of TAB_SIZE setting
* is performed again. It is finished by reaching the null value for the kitClass.
* The null value can be also used as the kitClass parameter value.
* In a more general look not only the kit-class hierarchy could be used
* in <tt>Settings</tt>. Any class inheritance hierarchy could be used here
* having the null as the common root.
*
* This way the inheritance of the setting values is guaranteed. By changing
* the setting value on the BaseKit level (or even on the null level),
* all the kit classes that don't
* override the particular setting are affected.
*
* settingName - name of the setting to change. All the currently available
* setting names are defined as public String constants in Settings class.
*
* newValue - new value for the setting. It must be always an object even
* if the setting is logicaly the basic datatype such as int (java.lang.Integer
* would be used in this case). A particular class types that can be used for
* the value of the settings are documented for each setting.
*
* WARNING! Please read carefully the description for each option you're
* going to change as you can make the editor stop working if you'll
* change the setting in a wrong way.
*
* @author Miloslav Metelka
* @version 1.00
*/
public class Settings {
/** Tab size for the document.
* Values: java.lang.Integer instances
*/
public static final String TAB_SIZE = "tab-size"; // NOI18N
/** Whether do tab to spaces expansion. The number of spaces to substitute
* per one tab is determined by SPACES_PER_TAB setting.
* Values: java.lang.Boolean instances
*/
public static final String EXPAND_TABS = "expand-tabs"; // NOI18N
/** How many spaces substitute per one typed tab. This parameter has
* effect only when EXPAND_TABS setting is set to true.
* This parameter has no influence on how
* the existing tabs are displayed.
* Values: java.lang.Integer instances
*/
public static final String SPACES_PER_TAB = "spaces-per-tab"; // NOI18N
/** Acceptor that recognizes the identifier characters.
* If set it's used instead of the default Syntax.isIdentifierPart() call.
* Values: org.netbeans.editor.Acceptor instances
*/
public static final String IDENTIFIER_ACCEPTOR = "identifier-acceptor"; // NOI18N
/** Acceptor that recognizes the whitespace characters.
* If set it's used instead of the default Syntax.isWhitespace() call.
* Values: org.netbeans.editor.Acceptor instances
*/
public static final String WHITESPACE_ACCEPTOR = "whitespace-acceptor"; // NOI18N
/** Map of the string abbreviations. The second string (value) means
* the full version of the first string (key).
* Values: java.util.Map instances holding
* [abbrev-string, expanded-abbrev-string] pairs
*/
public static final String ABBREV_MAP = "abbrev-map"; // NOI18N
/** Map of the action abbreviations. The second string (value) is
* the name of the action to execute. The action must be available
* in the kit actions. It can be added through <tt>CUSTOM_ACTION_LIST</tt>.
* The original abbreviation string
* is first removed from the text before the action is executed.
* If there is the same abbreviation in the <tt>ABBREV_MAP</tt> map
* it has a precedence over the <tt>ABBREV_ACTION_MAP</tt>.
* Values: java.util.Map instances holding
* [abbrev-string, name-of-the-action-to-execute] pairs
*/
public static final String ABBREV_ACTION_MAP = "abbrev-action-map"; // NOI18N
/** Acceptor checking whether abbreviation should be attempted
* after the appropriate character was typed.
* Values: org.netbeans.editor.Acceptor instances
*/
public static final String ABBREV_EXPAND_ACCEPTOR = "abbrev-expand-acceptor"; // NOI18N
/** Acceptor checking whether typed character that performed
* abbreviation expansion should be added to the text or not.
* Values: org.netbeans.editor.Acceptor instances
*/
public static final String ABBREV_ADD_TYPED_CHAR_ACCEPTOR
= "abbrev-add-typed-char-acceptor"; // NOI18N
/** Acceptor checking whether typed character should reset
* abbreviation accounting. By default all non-letterOrDigit chars
* reset the abbreviation accounting.
* Values: org.netbeans.editor.Acceptor instances
*/
public static final String ABBREV_RESET_ACCEPTOR = "abbrev-reset-acceptor"; // NOI18N
/** Type of caret for insert mode.
* Values: java.lang.String instances
* Currently supported types are:
* org.netbeans.editor.BaseCaret.LINE_CARET - default 2point caret
* org.netbeans.editor.BaseCaret.THIN_LINE_CARET - swing like thin caret
* org.netbeans.editor.BaseCaret.BLOCK_CARET - block covering whole character
*/
public static final String CARET_TYPE_INSERT_MODE = "caret-type-insert-mode"; // NOI18N
/** Type of caret for over write mode.
* Values: java.lang.String instances
* Currently supported types are:
* org.netbeans.editor.BaseCaret.LINE_CARET - default 2point caret
* org.netbeans.editor.BaseCaret.THIN_LINE_CARET - swing like thin caret
* org.netbeans.editor.BaseCaret.BLOCK_CARET - block covering whole character
*/
public static final String CARET_TYPE_OVERWRITE_MODE = "caret-type-overwrite-mode"; // NOI18N
/** Will the insert mode caret be italicized if the underlying font
* is italic?
* Values: java.lang.Boolean instances
*/
public static final String CARET_ITALIC_INSERT_MODE = "caret-italic-insert-mode"; // NOI18N
/** Will the overwrite mode caret be italicized if the underlying font
* is italic?
* Values: java.lang.Boolean instances
*/
public static final String CARET_ITALIC_OVERWRITE_MODE = "caret-italic-overwrite-mode"; // NOI18N
/** Caret color for insert mode.
* Values: java.awt.Color instances
*/
public static final String CARET_COLOR_INSERT_MODE = "caret-color-insert-mode"; // NOI18N
/** Caret color for overwrite mode.
* Values: java.awt.Color instances
*/
public static final String CARET_COLOR_OVERWRITE_MODE = "caret-color-overwrite-mode"; // NOI18N
/** Caret blink rate in milliseconds.
* Values: java.lang.Integer
*/
public static final String CARET_BLINK_RATE = "caret-blink-rate"; // NOI18N
/** Whether to display line numbers on the left part of the screen.
* Values: java.lang.Boolean instances
*/
public static final String LINE_NUMBER_VISIBLE = "line-number-visible"; // NOI18N
/** Whether to display line numbers when printing to the printer.
* Values: java.lang.Boolean instances
*/
public static final String PRINT_LINE_NUMBER_VISIBLE = "print-line-number-visible"; // NOI18N
/** How much should the view jump when scrolling goes off the screen.
* Insets are used so that it can be specified for each direction specifically.
* Each inset value can be positive or negative. The positive value means
* the number of lines for the top and the bottom and the number of characters
* for the left and the right. The negative value means percentage of the editor
* component height for the top and the bottom and percentage of the editor
* component width for the left and the right.
* Values: java.awt.Insets instances
*/
public static final String SCROLL_JUMP_INSETS = "scroll-jump-insets"; // NOI18N
/** How much space must be reserved in each direction for the find operation.
* It's here to ensure the found information will be visible in some
* context around it.
* Insets are used so that it can be specified for each direction specifically.
* Each inset value can be positive or negative. The positive value means
* the number of lines for the top and the bottom and the number of characters
* for the left and the right. The negative value means percentage of the editor
* component height for the top and the bottom and percentage of the editor
* component width for the left and the right.
* Values: java.awt.Insets instances
*/
public static final String SCROLL_FIND_INSETS = "scroll-find-insets"; // NOI18N
/** How much space will be added additionaly when the component needs to be
* resized.
* Each dimension value can be positive or negative. The positive value means
* the number of lines for the height and the number of characters
* for the width. The negative value means percentage of the editor
* component height for the height and percentage of the editor
* component width for the width.
* Values: java.awt.Dimension instances
*/
public static final String COMPONENT_SIZE_INCREMENT = "component-size-increment"; // NOI18N
/** Margin for the editor component
* Values: java.awt.Insets instances
*/
public static final String MARGIN = "margin"; // NOI18N
/** Margin on the left and right side of the line number.
* It's used only when line numbers are visible. The top and bottom values
* are ignored.
* Values: java.awt.Insets instances
*/
public static final String LINE_NUMBER_MARGIN = "line-number-margin"; // NOI18N
/** Rendering hints to be used for the painting.
* Values: java.util.Map instances
*/
public static final String RENDERING_HINTS = "rendering-hints"; // NOI18N
/** Key binding list for particular kit.
* Values: java.util.List instances holding
* javax.swing.JTextComponent.KeyBinding instances
* or org.netbeans.editor.MultiKeyBinding instances
*/
public static final String KEY_BINDING_LIST = "key-bindings"; // NOI18N
/** Whether the input-methods should be enabled.
* Values: java.lang.Boolean
*/
public static final String INPUT_METHODS_ENABLED = "input-methods-enabled"; // NOI18N
/** Float constant by which the height of the character obtained from
* the font is multiplied. It defaults to 1.
* Values: java.lang.Float instances
*/
public static final String LINE_HEIGHT_CORRECTION = "line-height-correction"; // NOI18N
/* Find properties.
* They are read by FindSupport when its instance is being initialized.
* FIND_WHAT: java.lang.String - search expression
* FIND_REPLACE_BY: java.lang.String - replace string
* FIND_HIGHLIGHT_SEARCH: java.lang.Boolean - highlight matching strings in text
* FIND_INC_SEARCH: java.lang.Boolean - show matching strings immediately
* FIND_BACKWARD_SEARCH: java.lang.Boolean - search in backward direction
* FIND_WRAP_SEARCH: java.lang.Boolean - if end of doc reached, start from begin
* FIND_MATCH_CASE: java.lang.Boolean - match case of letters
* FIND_SMART_CASE: java.lang.Boolean - case insensitive search if FIND_MATCH_CASE
* is false and all letters of FIND_WHAT are small, case sensitive otherwise
* FIND_WHOLE_WORDS: java.lang.Boolean - match only whole words
* FIND_REG_EXP: java.lang.Boolean - use regular expressions in search expr
* FIND_HISTORY: java.util.List - History of search expressions
* FIND_HISTORY_SIZE: java.lang.Integer - Maximum size of the history
*/
public static final String FIND_WHAT = "find-what"; // NOI18N
public static final String FIND_REPLACE_WITH = "find-replace-with"; // NOI18N
public static final String FIND_HIGHLIGHT_SEARCH = "find-highlight-search"; // NOI18N
public static final String FIND_INC_SEARCH = "find-inc-search"; // NOI18N
public static final String FIND_INC_SEARCH_DELAY = "find-inc-search-delay"; // NOI18N
public static final String FIND_BACKWARD_SEARCH = "find-backward-search"; // NOI18N
public static final String FIND_WRAP_SEARCH = "find-wrap-search"; // NOI18N
public static final String FIND_MATCH_CASE = "find-match-case"; // NOI18N
public static final String FIND_SMART_CASE = "find-smart-case"; // NOI18N
public static final String FIND_WHOLE_WORDS = "find-whole-words"; // NOI18N
public static final String FIND_REG_EXP = "find-reg-exp"; // NOI18N
public static final String FIND_HISTORY = "find-history"; // NOI18N
public static final String FIND_HISTORY_SIZE = "find-history-size"; // NOI18N
/** Number of characters that can be searched. If the value is larger
* than the document size, the document is used but the next document
* will not be used. The zero value disables the word match completely.
* Specify Integer.MAX_VALUE to search all the documents regardless
* of the size.
* Values: java.lang.Integer instances
*/
public static final String WORD_MATCH_SEARCH_LEN = "word-match-search-len"; // NOI18N
/** Wrap the word match searching
* on current document after it reaches the end/begining of
* current document. All the other documents except the current (first) one
* are searched from begining in forward direction.
* Values: java.lang.Boolean instances
*/
public static final String WORD_MATCH_WRAP_SEARCH = "word-match-wrap-search"; // NOI18N
/** Word list that is searched as last resort in word matching.
* It can contain the words that are used often by the user.
* If this property is set, these words are searched regardless
* of WORD_MATCH_SEARCH_LEN setting.
* Values: java.lang.String instances
*/
public static final String WORD_MATCH_STATIC_WORDS = "word-match-static-words"; // NOI18N
/** Whether to use case sensitive search or not.
* Values: java.lang.Boolean instances
*/
public static final String WORD_MATCH_MATCH_CASE = "word-match-match-case"; // NOI18N
/** Whether to use case insensitive search if all the letters are small
* and case sensitive search if at least one letter is capital.
* Values: java.lang.Boolean instances
*/
public static final String WORD_MATCH_SMART_CASE = "word-match-smart-case"; // NOI18N
/** Whether the word matching should return the match even if the matching
* word has only one char. The WORD_MATCH_MATCH_CASE setting is ignored
* in case this setting is on.
* Values: java.lang.Boolean instances
*/
public static final String WORD_MATCH_MATCH_ONE_CHAR = "word-match-match-one-char"; // NOI18N
/** List of actions that will be added to the standard list of actions
* for the particular kit. Using this mechanism, user can add actions
* and possibly map them to the keys without overriding kit classes.
* NOTICE!: This option has INCREMENTAL HANDLING, i.e. current kit list but also
* all the super kit lists are used. For example if there is a list of actions
* both for JavaKit and BaseKit classes, both list of actions will be added
* and BaseKit actions will be added first.
* Values: java.util.List instances
*/
public static final String CUSTOM_ACTION_LIST = "custom-action-list"; // NOI18N
/** List of actions which is executed when
* editor kit is installed into component. Actions are executed one by one
* in the order they occur in the list.
* At the time the kit is installed, the document is not yet assigned.
* To perform some actions on document, use the DOC_INSTALL_ACTION_LIST.
* NOTICE!: This option has INCREMENTAL HANDLING, i.e. current kit list but also
* all the super kit lists are used. For example if there is a list of actions
* both for JavaKit and BaseKit classes, both list of actions will be executed
* and JavaKit actions will be executed first.
* Values: java.util.List instances
*/
public static final String KIT_INSTALL_ACTION_NAME_LIST = "kit-install-action-name-list"; // NOI18N
/** List of actions that are executed when
* editor kit is being removed from component. Actions are executed one by one
* in the order they occur in the list.
* NOTICE!: This option has INCREMENTAL HANDLING, i.e. current kit list but also
* all the super kit lists are used. For example if there is a list of actions
* both for JavaKit and BaseKit classes, both list of actions will be executed
* and JavaKit actions will be executed first.
* Values: java.util.List instances
*/
public static final String KIT_DEINSTALL_ACTION_NAME_LIST = "kit-deinstall-action-name-list"; // NOI18N
/** List of actions which is executed when
* the new document is installed into component. Actions are executed one by one
* in the order they occur in the list.
* NOTICE!: This option has INCREMENTAL HANDLING, i.e. current kit list but also
* all the super kit lists are used. For example if there is a list of actions
* both for JavaKit and BaseKit classes, both list of actions will be executed
* and JavaKit actions will be executed first.
* Values: java.util.List instances
*/
public static final String DOC_INSTALL_ACTION_NAME_LIST = "doc-install-action-name-list"; // NOI18N
/** List of the action names that should be shown in the popup menu.
* Null name means separator.
* Values: java.util.List containing java.lang.String instances
*/
public static final String POPUP_MENU_ACTION_NAME_LIST = "popup-menu-action-name-list"; // NOI18N
/** Whether status bar should be visible or not.
* Values: java.lang.Boolean instances
*/
public static final String STATUS_BAR_VISIBLE = "status-bar-visible"; // NOI18N
/** Delay for updating information about caret in the status bar.
* Values: java.lang.Integer instances
*/
public static final String STATUS_BAR_CARET_DELAY = "status-bar-caret-delay"; // NOI18N
/** Whether the line displaying the text limit should be displayed.
* Values: java.lang.Boolean instances
*/
public static final String TEXT_LIMIT_LINE_VISIBLE = "text-limit-line-visible"; // NOI18N
/** Which color should be used for the line showing the text limit.
* Values: java.awt.Color instances
*/
public static final String TEXT_LIMIT_LINE_COLOR = "text-limit-line-color"; // NOI18N
/** After how many characters the text limit line should be displayed.
* Values: java.awt.Integer instances
*/
public static final String TEXT_LIMIT_WIDTH = "text-limit-width"; // NOI18N
/** Whether the home key should go to column 1 or first go to text start
* on the given line and then to the column 1.
* Values: java.lang.Boolean
*/
public static final String HOME_KEY_COLUMN_ONE = "home-key-column-one"; // NOI18N
/** Finder for finding the next word. If it's not set,
* the <tt>FinderFactory.NextWordFwdFinder</tt> is used.
* Values: org.netbeans.editor.Finder
*/
public static final String NEXT_WORD_FINDER = "next-word-finder"; // NOI18N
/** Finder for finding the previous word. If it's not set,
* the <tt>FinderFactory.WordStartBwdFinder</tt> is used.
* Values: org.netbeans.editor.Finder
*/
public static final String PREVIOUS_WORD_FINDER = "previous-word-finder"; // NOI18N
/** Whether the word move should stop on the '\n' character. This setting
* affects both the
* Values: java.lang.Boolean
*/
public static final String WORD_MOVE_NEW_LINE_STOP = "word-move-new-line-stop"; // NOI18N
/** Indentation and text formatting. User can create its own formatter
* by subclassing Formatter.
* Values: org.netbeans.editor.Formatter instances
*/
public static final String FORMATTER = "formatter"; // NOI18N
/** Hot characters after which the line should be re-tested for indentation
* such as '}' or ':'.
* Values: org.netbeans.editor.Acceptor instances
*/
public static final String INDENT_HOT_CHAR_ACCEPTOR = "indent-hot-char"; // NOI18N
/** Shift-width says how many spaces should indentation use
* to delimit next level of code. This setting is independent of TAB_SIZE.
* Values: java.lang.Integer instances
*/
public static final String INDENT_SHIFT_WIDTH = "indent-shift-width"; // NOI18N
/** Whether to trim the white space characters (except '\n') from
* the end of the line.
* Values: java.lang.Boolean instances
*/
// public static final String TRIM_SPACES = "trim-spaces"; // NOI18N
/** Buffer size for reading into the document from input stream or reader.
* Values: java.lang.Integer
* WARNING! This is critical parameter for editor functionality.
* Please see DefaultSettings.java for values of this setting
*/
public static final String READ_BUFFER_SIZE = "read-buffer-size"; // NOI18N
/** Buffer size for writing from the document to output stream or writer.
* Values: java.lang.Integer instances
* WARNING! This is critical parameter for editor functionality.
* Please see DefaultSettings.java for values of this setting
*/
public static final String WRITE_BUFFER_SIZE = "write-buffer-size"; // NOI18N
/** Read mark distance is used when performing initial read
* of the document. It denotes the distance in chars of two adjacent
* syntax marks inserted into the document.
* Values: java.lang.Integer instances
* WARNING! This is critical parameter for editor functionality.
* Please see DefaultSettings.java for values of this setting
*/
public static final String READ_MARK_DISTANCE = "read-mark-distance"; // NOI18N
/** Implicit mark distance for inserting to the document.
* If the insert is made then the distance between nearest syntax
* marks around insertion point is checked and if it's greater
* than the max mark distance then another mark(s) are inserted
* automatically with the distance given by this setting.
* Values: java.lang.Integer instances instances
* WARNING! This is critical parameter for editor functionality.
* Please see DefaultSettings.java for values of this setting
*/
public static final String MARK_DISTANCE = "mark-distance"; // NOI18N
/** Maximum mark distance. When there is an insertion done in document
* and the distance between marks gets greater than this setting, another
* mark will be inserted automatically.
* Values: java.lang.Integer instances
* WARNING! This is critical parameter for editor functionality.
* Please see DefaultSettings.java for values of this setting
*/
public static final String MAX_MARK_DISTANCE = "max-mark-distance"; // NOI18N
/** Minimum mark distance for removals. When there is a removal done
* in document and it makes the marks to get closer than this value, then
* the marks the additional marks that are closer than the distance
* given by this setting are removed automatically.
* Values: java.lang.Integer instances
* WARNING! This is critical parameter for editor functionality.
* Please see DefaultSettings.java for values of this setting
*/
public static final String MIN_MARK_DISTANCE = "min-mark-distance"; // NOI18N
/** Size of one batch of characters loaded into syntax segment
* when updating syntax marks. It prevents checking and loading
* of syntax segment at every syntax mark. Instead it loads
* at least the amount of characters given by this setting.
* This whole process is done only in case the changes in syntax
* extend the end of current line. If the syntax changes don't
* extend to the next line, this setting has no effect.
* Values: java.lang.Integer instances
* WARNING! This is critical parameter for editor functionality.
* Please see DefaultSettings.java for values of this setting
*/
public static final String SYNTAX_UPDATE_BATCH_SIZE = "syntax-update-batch-size"; // NOI18N
/** How many lines should be processed at once in the various text
* processing. This is used for example when processing the text
* by syntax scanner.
*/
public static final String LINE_BATCH_SIZE = "line-batch-size"; // NOI18N
/** List of the names of the additional colorings (except the base ones).
* The coloring names are without the suffix just like the predefined coloring names.
* Values: java.util.List instances
*/
public static final String COLORING_NAME_LIST = "coloring-name-list"; // NOI18N
/** List of the coloring names that should be excluded from the font uniformity check.
* At the begining the extended-UI needs to determine whether all the colorings
* that will be used to color the component have the same fixed-size font. If so
* the faster drawing method is used. This setting should contain the names of all
* the colorings that are not used in the editor component for example the status bar
* colorings.
* Values: java.util.List instances
*/
public static final String UNIFORM_FONT_EXCLUSION_LIST = "uniform-font-exclusion-list"; // NOI18N
/** Suffix added to the coloring settings. The resulting name is used
* as the name of the setting.
*/
public static final String COLORING_NAME_SUFFIX = "-coloring"; // NOI18N
/** Suffix added to the print coloring settings. The resulting name is used
* as the name of the setting.
*/
public static final String COLORING_NAME_PRINT_SUFFIX = "-print-coloring"; // NOI18N
/** Default coloring for the drawing. */
public static final String DEFAULT_COLORING = "default"; // NOI18N
/** Coloring that will be used for line numbers displayed on the left
* side on the screen.
*/
public static final String LINE_NUMBER_COLORING = "line-number"; // NOI18N
/** Coloring used for guarded blocks */
public static final String GUARDED_COLORING = "guarded"; // NOI18N
/** Coloring used for selection */
public static final String SELECTION_COLORING = "selection"; // NOI18N
/** Coloring used for highlight search */
public static final String HIGHLIGHT_SEARCH_COLORING = "highlight-search"; // NOI18N
/** Coloring used for incremental search */
public static final String INC_SEARCH_COLORING = "inc-search"; // NOI18N
/** Coloring used to highlight the row where the caret resides */
public static final String HIGHLIGHT_ROW_COLORING = "highlight-row"; // NOI18N
/** Coloring used to highlight the matching bracket */
public static final String HIGHLIGHT_BRACKET_COLORING = "highlight-bracket"; // NOI18N
/** Coloring used for bookmark lines */
public static final String BOOKMARK_COLORING = "bookmark"; // NOI18N
/** Coloring used for the status bar */
public static final String STATUS_BAR_COLORING = "status-bar"; // NOI18N
/** Coloring used to mark important text in the status bar */
public static final String STATUS_BAR_BOLD_COLORING = "status-bar-bold"; // NOI18N
/** List of Initializers
* @associates Initializer*/
private static final List initializerList = new ArrayList();
/** List of Filters
* @associates Filter*/
private static final List filterList = new ArrayList();
/** [kit-class, map-of-settings] pairs
* @associates Map*/
private static final Map kit2Maps = new HashMap();
/** Support for firing change events */
private static final WeakEventListenerList listenerList = new WeakEventListenerList();
/** Internal map instance signing that initializer returned null
* map for particular kit. To sign this fact and not query initializer
* again, this simple map is used.
*/
private static final Map NULL_MAP = new HashMap(1);
private static boolean firingEnabled = true;
private Settings() {
// no instances allowed
}
/** Add initializer instance to the list of current initializers.
* If there are already existing editor components,
* and you want to apply the settings provided by this new initializer
* to these existing
* components, you can call reset(). However all the changes
* that were made explicitly by calling setValue() will be lost.
*
* @param i initializer to add to the current list of initializers
*/
public static synchronized void addInitializer(Initializer i) {
initializerList.add(i);
}
/** Add filter instance to the list of current filters.
* If there are already existing editor components,
* and you want to apply the changes that this filter makes
* to these existing
* components, you can call reset(). However all the changes
* that were made explicitly by calling setValue() will be lost.
*
* @param f filter to add to the list of the filters
*/
public static synchronized void addFilter(Filter f) {
filterList.add(f);
}
/** Get the property by searching the given kit class settings and if not
* found then the settings for super class and so on.
* @param kitClass editor kit class for which the value of setting should
* be retrieved. The null can be used as the root of the whole hierarchy.
* (which is top of kit class hierarchy) before the processing begins.
* @param settingName name of the setting for which the value should
* be retrieved
* @return the value of the setting
*/
public static synchronized Object getValue(Class kitClass, String settingName) {
Object value = null;
Class kc = kitClass;
while (true) {
Map map = getKitMap(kc, false);
if (map != null) {
value = map.get(settingName);
if (value instanceof Substituter) {
value = ((Substituter)value).getValue(kitClass, settingName);
}
if (value != null) {
break;
}
}
if (kc == null) {
break;
}
kc = kc.getSuperclass();
}
// filter the value if necessary
int cnt = filterList.size();
for (int i = 0; i < cnt; i++) {
value = ((Filter)filterList.get(i)).filterValue(kitClass, settingName, value);
}
return value;
}
/** Get array of KitAndValue objects sorted from the given kit class to its
* deepest superclass and the last member can be filled whether there
* is global setting (kit class of that member would be null).
* This method is useful for objects like keymaps that
* need to create all the parent keymaps to work properly.
* @param kitClass editor kit class for which the value of setting should
* be retrieved. The null can be used as the root of the whole hierarchy.
* @param settingName name of the setting for which the value should
* be retrieved
* @return the array containing KitAndValue instances describing the particular
* setting's value on the specific kit level.
*/
public static synchronized KitAndValue[] getKitAndValueArray(Class kitClass,
String settingName) {
ArrayList kavList = new ArrayList();
Class kc = kitClass;
while (true) {
Map map = getKitMap(kc, false);
if (map != null) {
Object value = map.get(settingName);
if (value instanceof Substituter) {
value = ((Substituter)value).getValue(kitClass, settingName);
}
if (value != null) {
kavList.add(new KitAndValue(kc, value));
}
}
if (kc == null) {
break;
}
kc = kc.getSuperclass();
}
KitAndValue[] kavArray = (KitAndValue[])kavList.toArray(
new KitAndValue[kavList.size()]);
// filter the value if necessary
int cnt = filterList.size();
for (int i = 0; i < cnt; i++) {
kavArray = ((Filter)filterList.get(i)).filterKitAndValueArray(
kitClass, settingName, kavArray);
}
return kavArray;
}
/** Set the new value for property on kit level. The old and new values
* are compared and if they are equal the setting is not changed and
* nothing is fired.
*
* @param kitClass editor kit class for which the value of setting should
* be set. The null can be used as the root of the whole hierarchy.
* @param settingName the string used for searching the value
* @param newValue new value to set for the property; the value can
* be null to clear the value for the specified kit
*/
public static synchronized void setValue(Class kitClass, String settingName,
Object newValue) {
Map map = getKitMap(kitClass, true);
Object oldValue = map.get(settingName);
if (oldValue == null && newValue == null
|| (oldValue != null && oldValue.equals(newValue))
) {
return; // no change
}
if (newValue != null) {
map.put(settingName, newValue);
} else {
map.remove(settingName);
}
fireSettingsChange(kitClass, settingName, oldValue, newValue);
}
/** Don't change the value of the setting, but fire change
* event. This is useful when there's internal change in the value object
* of some setting.
*/
public static synchronized void touchValue(Class kitClass, String settingName) {
fireSettingsChange(kitClass, settingName, null, null); // kit class currently not used
}
/** Set the value for the current kit and propagate it to all
* the children of the given kit by removing
* the possible values for the setting from the children kit setting maps.
* Note: This call only affects the settings for the kit classes for which
* the kit setting map with the setting values currently exists, i.e. when
* there was at least one getValue() or setValue() call performed for any
* setting on that particular kit class level. Other kit classes maps
* will be initialized by the particular initializer(s) as soon as
* the first getValue() or setValue() will be performed for them.
* However that future process will not be affected by the current
* propagateValue() call.
* This method is useful for the visual options that always set
* the value on all the kit levels without regard whether it's necessary or not.
* If the value is then changed for the base kit, it's not propagated
* naturally as there's a special setting
* This method enables
*
* The current implementation always fires the change regardless whether
* there was real change in setting value or not.
* @param kitClass editor kit class for which the value of setting should
* be set. When given null, it's automatically set to BaseKit.class
* (which is top of kit class hierarchy) before the processing begins.
* Therefore you get exactly the same results for kitClass == null and
* kitClass == BaseKit.class.
* @param settingName the string used for searching the value
* @param newValue new value to set for the property; the value can
* be null to clear the value for the specified kit
*/
public static synchronized void propagateValue(Class kitClass,
String settingName, Object newValue) {
Map map = getKitMap(kitClass, true);
if (newValue != null) {
map.put(settingName, newValue);
} else {
map.remove(settingName);
}
// resolve kits
Iterator it = kit2Maps.entrySet().iterator();
while(it.hasNext()) {
Map.Entry me = (Map.Entry)it.next();
Class kc = (Class)me.getKey();
if (kitClass != kc && (kitClass == null || kitClass.isAssignableFrom(kc))) {
((Map)me.getValue()).remove(settingName);
}
}
fireSettingsChange(null, settingName, null, null);
}
/** Run the given runnable. All the changes in the settings are not fired until
* the whole runnable completes. Nesting of <tt>update()</tt> call is allowed.
* Only one firing is performed after the whole runnable completes
* using the 'null triple'.
*/
public static synchronized void update(Runnable r) {
boolean turnedOff = firingEnabled;
firingEnabled = false;
try {
r.run();
} finally {
if (turnedOff) {
firingEnabled = true;
fireSettingsChange(null, null, null, null);
}
}
}
/** Reset all the settings and fire the change of the settings
* so that all the listeners will be notified and will reload
* the settings.
* The settings that were changed using setValue() and propagateValue()
* are lost. Initializers will be asked for the settings values when
* necessary.
*/
public static void reset() {
kit2Maps.clear();
fireSettingsChange(null, null, null, null);
}
/** Add weak listener to listen to change of any property. The caller must
* hold the listener object in some instance variable to prevent it
* from being garbage collected.
*/
public static void addSettingsChangeListener(SettingsChangeListener l) {
listenerList.add(SettingsChangeListener.class, l);
}
/** Remove listener for changes in properties */
public static void removeSettingsChangeListener(SettingsChangeListener l) {
listenerList.remove(SettingsChangeListener.class, l);
}
private static void fireSettingsChange(Class kitClass, String settingName,
Object oldValue, Object newValue) {
if (firingEnabled) {
SettingsChangeListener[] listeners = (SettingsChangeListener[])
listenerList.getListeners(SettingsChangeListener.class);
SettingsChangeEvent evt = new SettingsChangeEvent(Settings.class,
kitClass, settingName, oldValue, newValue);
for (int i = 0; i < listeners.length; i++) {
listeners[i].settingsChange(evt);
}
}
}
/** Get (and possibly create) kit map for particular kit */
private static Map getKitMap(Class kitClass, boolean forceCreation) {
Map kitMap = (Map)kit2Maps.get(kitClass);
if (kitMap == null) {
Iterator iter = initializerList.iterator();
while (iter.hasNext()) {
Initializer i = (Initializer)iter.next();
kitMap = i.updateSettingsMap(kitClass, kitMap);
}
if (kitMap == null) { // initializer refused creation
kitMap = NULL_MAP; // initializer will not be called again
}
kit2Maps.put(kitClass, kitMap);
}
if (kitMap == NULL_MAP) {
if (!forceCreation) {
return null;
} else {
kitMap = new HashMap(20); // create empty map
kit2Maps.put(kitClass, kitMap);
}
}
return kitMap;
}
/** Kit class and value pair */
public static class KitAndValue {
public Class kitClass;
public Object value;
public KitAndValue(Class kitClass, Object value) {
this.kitClass = kitClass;
this.value = value;
}
}
/** Initializer of the settings updates the map filled
* with settings for the particular kit class when asked.
* If the settings are being initialized all the initializers registered
* by the <tt>Settings.addInitializer()</tt> are being asked to update'
* the settings-map through calling their <tt>updateSettingsMap()</tt>.
* If there is more initializers they are all called to update
* the settings-map in the order they were added.
*/
public static interface Initializer {
/** Update map filled with the settings from the previous initializer
* @param kitClass kit class for which the settings are being updated.
* It is always non-null value.
* @param m map with settings to update. It can be null if all
* the previous initializers didn't need to create any settings
* for the given kit class.
* @return map containing the desired settings or null if no settings
* are defined for the given kit
*/
public Map updateSettingsMap(Class kitClass, Map m);
}
/** Substituter can be used in cases when value of some setting
* depends on the value for other setting and it allows to compute
* the value dynamically based on the other setting(s) value.
* The <tt>Substituter</tt> instance can be used as the value
* in the <tt>Settings.setValue()</tt> call. In that case the call
* to the <tt>Settings.getValue()</tt> call will 'evaluate' the substituter
* by calling its <tt>getValue()</tt>.
*/
public static interface Substituter {
/** Compute the particular setting's value.
* @param kitClass kit class for which the setting is being retrieved.
* @param settingName name of the setting to retrieve. Although the substituter
* are usually constructed only for the concrete setting, this parameter
* allows creation of the substituter for multiple settings.
* @return the value for the requested setting. The substitution
* is not attempted again, so the return value cannot be another
* Substituter instance. If the returned value is null, the same
* action is taken as if there would no value set on the particular
* kit level.
*
*/
public Object getValue(Class kitClass, String settingName);
}
/** Filter is applied on every value or KitAndValue pairs returned from getValue().
* The filter can be registered by calling <tt>Settings.addFilter()</tt>.
* Each call to <tt>Settings.getValue()</tt> will first retrieve the value and
* then call the <tt>Filter.filterValue()</tt> to get the final value. Each call
* to <tt>Settings.getKitAndValueArray()</tt> will first retrieve the kit-and-value
* array and then call the <tt>Filter.filterKitAndValueArray()</tt>.
* If more filters are registered they are all used in the order they were added.
*/
public static interface Filter {
/** Filter single value. The value can be substituted here.
*/
public Object filterValue(Class kitClass, String settingName, Object value);
/** Filter array of kit and value pairs. The pairs can be completely
* substituted with an array with different length and different members.
*/
public KitAndValue[] filterKitAndValueArray(Class kitClass, String settingName,
KitAndValue[] kavArray);
}
}
/*
* Log
* 46 Gandalf-post-FCS1.44.1.0 3/8/00 Miloslav Metelka
* 45 Gandalf 1.44 1/13/00 Miloslav Metelka
* 44 Gandalf 1.43 1/11/00 Miloslav Metelka
* 43 Gandalf 1.42 1/6/00 Miloslav Metelka
* 42 Gandalf 1.41 1/4/00 Miloslav Metelka
* 41 Gandalf 1.40 12/28/99 Miloslav Metelka
* 40 Gandalf 1.39 11/24/99 Miloslav Metelka
* 39 Gandalf 1.38 11/14/99 Miloslav Metelka
* 38 Gandalf 1.37 11/10/99 Miloslav Metelka
* 37 Gandalf 1.36 11/8/99 Miloslav Metelka
* 36 Gandalf 1.35 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 35 Gandalf 1.34 10/6/99 Miloslav Metelka
* 34 Gandalf 1.33 9/30/99 Miloslav Metelka
* 33 Gandalf 1.32 9/10/99 Miloslav Metelka
* 32 Gandalf 1.31 8/27/99 Miloslav Metelka
* 31 Gandalf 1.30 8/19/99 Miloslav Metelka
* 30 Gandalf 1.29 8/18/99 Miloslav Metelka
* 29 Gandalf 1.28 8/17/99 Miloslav Metelka
* 28 Gandalf 1.27 7/29/99 Miloslav Metelka
* 27 Gandalf 1.26 7/26/99 Miloslav Metelka
* 26 Gandalf 1.25 7/22/99 Miloslav Metelka
* 25 Gandalf 1.24 7/21/99 Miloslav Metelka
* 24 Gandalf 1.23 7/20/99 Miloslav Metelka
* 23 Gandalf 1.22 7/9/99 Miloslav Metelka
* 22 Gandalf 1.21 7/2/99 Miloslav Metelka
* 21 Gandalf 1.20 6/29/99 Miloslav Metelka Scrolling and patches
* 20 Gandalf 1.19 6/25/99 Miloslav Metelka from floats back to ints
* 19 Gandalf 1.18 6/22/99 Miloslav Metelka
* 18 Gandalf 1.17 6/10/99 Miloslav Metelka
* 17 Gandalf 1.16 6/8/99 Miloslav Metelka
* 16 Gandalf 1.15 6/1/99 Miloslav Metelka
* 15 Gandalf 1.14 5/15/99 Miloslav Metelka fixes
* 14 Gandalf 1.13 5/13/99 Miloslav Metelka
* 13 Gandalf 1.12 5/7/99 Miloslav Metelka line numbering and fixes
* 12 Gandalf 1.11 5/5/99 Miloslav Metelka
* 11 Gandalf 1.10 4/23/99 Miloslav Metelka changes in settings
* 10 Gandalf 1.9 4/23/99 Miloslav Metelka Undo added and internal
* improvements
* 9 Gandalf 1.8 4/13/99 Ian Formanek Abbreviations are back
* (it is not possible to compile it under Startup as it depends on editor
* module)
* 8 Gandalf 1.7 4/11/99 Ian Formanek Added static method
* getAbbrevTable
* 7 Gandalf 1.6 4/11/99 Ian Formanek
* 6 Gandalf 1.5 4/8/99 Ian Formanek Abbreviations moved to
* Startup/Abbrevs.java
* 5 Gandalf 1.4 4/8/99 Miloslav Metelka
* 4 Gandalf 1.3 4/1/99 Miloslav Metelka
* 3 Gandalf 1.2 3/30/99 Miloslav Metelka
* 2 Gandalf 1.1 3/27/99 Miloslav Metelka
* 1 Gandalf 1.0 3/23/99 Miloslav Metelka
* $
*/